// -----------------------------------------------------------------------
//  <copyright file="EntityAuthorizationService.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://www.dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024-03-07 21:17</last-date>
// -----------------------------------------------------------------------

using System.Security.Claims;

using DaprPlus.Dependency;
using DaprPlus.Filters;
using DaprPlus.Identity;


namespace DaprPlus.Authorization.Application;

/// <summary>
/// 数据权限验证服务
/// </summary>
public class EntityAuthorizationService(IServiceProvider provider) : IAuthorizationService
{
    /// <summary>
    /// 获取 服务的资源类型
    /// </summary>
    public ResourceType ResourceType => ResourceType.Entity;

    /// <summary>
    /// 获取 当前用户信息
    /// </summary>
    private ClaimsPrincipal? CurrentUser => provider.GetCurrentUser();

    /// <summary>
    /// 获取 权限缓存
    /// </summary>
    private IAuthorizationCache AuthorizationCache => provider.GetRequiredService<IAuthorizationCache>();

    /// <summary>
    /// 获取 角色名称解析器
    /// </summary>
    private IRoleNameResolver RoleNameResolver => provider.GetRequiredService<IRoleNameResolver>();

    /// <summary>
    /// 获取 日志对象
    /// </summary>
    private ILogger Logger => provider.GetLogger(GetType());

    /// <summary>
    /// 验证当前用户对指定资源的执行权限
    /// </summary>
    /// <param name="resource">资源信息</param>
    /// <param name="authConfig">权限配置</param>
    /// <returns></returns>
    public async Task<AuthorizationResult> Authorize(IResource resource, AuthConfig authConfig)
    {
        // 验证资源类型和用户登录状态
        var validationResult = ValidateResourceAndAuthentication(resource, authConfig);
        if (validationResult != null)
        {
            return validationResult;
        }

        // 获取当前用户的角色
        var roleNames = CurrentUser!.Identity!.GetRoles();
        if (roleNames.Length == 0)
        {
            return Forbidden();
        }

        // 超级管理员，直接允许
        if (await IsTopAdminRole(roleNames))
        {
            return AuthorizationResult.Ok;
        }

        // 获取角色ID
        var roleIds = await RoleNameResolver.ResolveRoleIds(roleNames);
        if (roleIds.Length == 0)
        {
            return Forbidden();
        }

        // 传入的operation是 ResourceOperation.Action 的格式，
        // 1.当实体资源的允许操作中以枚举名开头只有1个时，说明该枚举没有其他子操作，使用枚举名来作为操作判断
        // 2.当实体资源的允许操作中以枚举名开头存在多个时，说明该枚举有其他子操作，使用完整操作名来作为操作判断
        var operation = authConfig.Operation;
        var opera = operation.Split('.')[0];
        var allowedOperations = resource.GetAllowedOperations();
        if (allowedOperations.Count(m => m.Code.StartsWith(opera)) == 1)
        {
            operation = opera;
        }

        // 获取并验证用户角色权限
        var authItems = await GetAndValidateRoleAuthItems(resource, operation, roleIds);
        if (authItems.Length == 0)
        {
            return Forbidden();
        }

        // 设置数据权限过滤条件
        await SetupDataAuthorizationFilters(operation, authItems);

        return new AuthorizationResult(AuthorizationStatus.Ok);
    }

    /// <summary>
    /// 验证资源类型和用户登录状态
    /// </summary>
    private AuthorizationResult? ValidateResourceAndAuthentication(IResource resource, AuthConfig authConfig)
    {
        if (resource.Type != ResourceType.Entity)
        {
            return new AuthorizationResult(AuthorizationStatus.Error, "传入的资源不是实体资源");
        }

        var currentUser = CurrentUser;
        switch (authConfig.AccessType)
        {
            case ResourceAccessType.None:
            case ResourceAccessType.Anonymous:
                return AuthorizationResult.Ok;

            case ResourceAccessType.LoggedIn:
                if (currentUser?.Identity?.IsAuthenticated != true)
                {
                    return new AuthorizationResult(AuthorizationStatus.Unauthorized, "此操作需要登录才能继续");
                }

                return AuthorizationResult.Ok;

            case ResourceAccessType.RoleLimit:
                if (currentUser?.Identity?.IsAuthenticated != true)
                {
                    return new AuthorizationResult(AuthorizationStatus.Unauthorized, "此操作需要登录才能继续");
                }

                break;
        }

        return null;
    }

    /// <summary>
    /// 获取并验证用户角色权限
    /// </summary>
    private async Task<ResourceAuthCacheItem[]> GetAndValidateRoleAuthItems(IResource resource, string operation, long[] roleIds)
    {
        // 获取资源的角色权限缓存
        var authItems = await AuthorizationCache.GetOrBuildResourceAuthorizationCache(resource);
        if (authItems.Length == 0)
        {
            // 没有任何角色配置，拒绝访问
            return [];
        }

        // 先收集用户所有角色对应的权限项
        authItems = authItems.Where(m => roleIds.Contains(m.RoleId)).ToArray();
        if (authItems.Length == 0)
        {
            // 用户角色没有任何权限配置
            return [];
        }
        return authItems.Where(authItem => authItem.Operations.Contains(operation)).ToArray();
    }

    /// <summary>
    /// 设置数据权限过滤条件
    /// </summary>
    private Task SetupDataAuthorizationFilters(string operation, ResourceAuthCacheItem[] authItems)
    {
        // 收集所有有效角色的数据权限过滤条件
        var scopedDict = provider.GetRequiredService<ScopedDictionary>();
        var validRoleIds = authItems.Select(m => m.RoleId).ToArray();
        scopedDict.ValidRoleIds = validRoleIds;

        // 合并所有角色的数据权限过滤条件
        var dataAuthFilterGroups = BuildDataAuthFilterGroups(operation, authItems);

        // 设置数据权限过滤条件
        scopedDict.DataAuthFilterGroups = dataAuthFilterGroups;

        return Task.CompletedTask;
    }

    /// <summary>
    /// 构建数据权限过滤条件组
    /// </summary>
    private Dictionary<string, FilterGroup> BuildDataAuthFilterGroups(string operation, ResourceAuthCacheItem[] authItems)
    {
        var dataAuthFilterGroups = new Dictionary<string, FilterGroup>();

        var combinedFilterGroup = BuildFilterGroupForOperation(operation, authItems);

        // 如果有过滤条件，添加到字典中
        if (combinedFilterGroup.Groups.Count > 0)
        {
            dataAuthFilterGroups[operation] = combinedFilterGroup;
        }

        return dataAuthFilterGroups;
    }

    /// <summary>
    /// 为指定操作构建过滤条件组
    /// </summary>
    private FilterGroup BuildFilterGroupForOperation(string operation, ResourceAuthCacheItem[] authItems)
    {
        // 对于每个操作，创建一个 OR 组合的过滤条件
        var combinedFilterGroup = new FilterGroup(FilterOperate.Or);

        // 收集所有角色对该操作的过滤条件
        foreach (var authItem in authItems)
        {
            if (authItem.Operations.Contains(operation) &&
                authItem.FilterGroups.TryGetValue(operation, out var filterGroup))
            {
                // 如果过滤条件不为空，添加到组合过滤条件中
                if (filterGroup.Rules.Count > 0 || filterGroup.Groups.Count > 0)
                {
                    combinedFilterGroup.Groups.Add(filterGroup);
                }
            }
        }

        return combinedFilterGroup;
    }

    private async Task<bool> IsTopAdminRole(string[] roleNames)
    {
        // 检查是否为最高管理员角色
        var topAdminRoleService = provider.GetService<ITopAdminRoleService>();
        return topAdminRoleService != null && await topAdminRoleService.IsTopAdminRole(roleNames);
    }

    private AuthorizationResult Forbidden(string? msg = null)
    {
        return new AuthorizationResult(AuthorizationStatus.Forbidden, msg ?? "当前用户没有足够的权限继续操作");
    }
}
